//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <core.h>
#include <_hal.h>
#include <_schedule.h>

//
// DzEventSet
//
DzEventSet::DzEventSet(
    uint_t uNumberOfEvents, DzEvent **pEvents,
    EventSetRefNode *pEventSetRefNodes)
{
    assert(1 < uNumberOfEvents);
    assert(pEvents);
    assert(pEventSetRefNodes);

    m_uNumberOfEvents = uNumberOfEvents;
    m_pEvents = pEvents;
    m_pEventSetRefNodes = pEventSetRefNodes;

    uint_t i;
    for (i = 0; i < m_uNumberOfEvents; i++) {
        m_pEventSetRefNodes[i].uIndex = i;
        m_pEventSetRefNodes[i].pEventSet = this;
    }

    uint32_t uFlags = SaveFlagsAndCli();
    for (i = 0; i < m_uNumberOfEvents; i++) {
        assert(m_pEvents[i]);
        m_pEvents[i]->BindEventSet(m_pEventSetRefNodes + i);
    }
    RestoreIF(uFlags);
}

DzEventSet::~DzEventSet()
{
    uint32_t uFlags = SaveFlagsAndCli();
    for (uint_t i = 0; i < m_uNumberOfEvents; i++) {
        m_pEvents[i]->UnbindEventSet(m_pEventSetRefNodes + i);
    }
    RestoreIF(uFlags);
}

// IMPORTANT: when disable interrupt
INLINE void DzEventSet::WaiterReadyToUninterruptibleSleep()
{
    register Thread *pCurrentThread = GetCurrentThread();

//    WillToInheritPriority(pCurrentThread);

    ReadyToUninterruptibleSleepOn(this);

    InsertWaiter(pCurrentThread);
}

// IMPORTANT: when disable interrupt
INLINE void DzEventSet::WaiterReadyToUninterruptibleSleep(
    DzTimer *pBindingTimer)
{
    assert(pBindingTimer);

    register Thread *pCurrentThread = GetCurrentThread();

//    WillToInheritPriority(pCurrentThread);

    ReadyToUninterruptibleSleepOn(this, pBindingTimer);

    pBindingTimer->_Start();
    InsertWaiter(pCurrentThread);
}

// IMPORTANT: when disable interrupt
INLINE void DzEventSet::WaiterReadyToSleep(WaitResult *pResult)
{
    assert(pResult);

    register Thread *pCurrentThread = GetCurrentThread();

//    WillToInheritPriority(pCurrentThread);

    ReadyToSleepOn(this, NULL, pResult);
    if (WaitResult_OK != *pResult) {
        return;
    }

    InsertWaiter(pCurrentThread);

    *pResult = WaitResult_OK;
}

// IMPORTANT: when disable interrupt
INLINE void DzEventSet::WaiterReadyToSleep(
    DzTimer *pBindingTimer, WaitResult *pResult)
{
    assert(pBindingTimer);
    assert(pResult);

    register Thread *pCurrentThread = GetCurrentThread();

//    WillToInheritPriority(pCurrentThread);

    ReadyToSleepOn(this, pBindingTimer, pResult);
    if (WaitResult_OK != *pResult) {
        return;
    }

    pBindingTimer->_Start();
    InsertWaiter(pCurrentThread);

    *pResult = WaitResult_OK;
}

void DzEventSet::UninterruptibleWait(Int32 *pIndex, EventState *pState)
{
    assert(pIndex);

    uint_t i, uState;

    uint32_t uFlags = SaveFlagsAndCli();
    for (i = 0; i < m_uNumberOfEvents; i++) {
        register DzEvent *pEvent = m_pEvents[i];

        if (pEvent->m_bManualReset) {
            if (UNSIGNALED == (uState = pEvent->m_uState)) {
                continue;
            }
        }
        else {
            if (UNSIGNALED == (uState = InterlockedExchange(
                    (Int32 *)&pEvent->m_uState, UNSIGNALED))) {
                continue;
            }
        }

        RestoreIF(uFlags);

        *pIndex = i;
        if (NULL != pState) *pState = uState;
        return;
    }

    WaiterReadyToUninterruptibleSleep();
    RestoreIF(uFlags);

    Schedule();

    *pIndex = GetCurrentThread()->GetNotifiedEventIndex();
    if (NULL != pIndex) *pIndex = GetCurrentThread()->GetEventState();
}

void DzEventSet::Wait(WaitResult *pResult, Int32 *pIndex, EventState *pState)
{
    assert(pResult);
    assert(pIndex);

    uint_t i, uState;

    uint32_t uFlags = SaveFlagsAndCli();
    for (i = 0; i < m_uNumberOfEvents; i++) {
        register DzEvent *pEvent = m_pEvents[i];

        if (pEvent->m_bManualReset) {
            if (UNSIGNALED == (uState = pEvent->m_uState)) {
                continue;
            }
        }
        else {
            if (UNSIGNALED == (uState = InterlockedExchange(
                    (Int32 *)&pEvent->m_uState, UNSIGNALED))) {
                continue;
            }
        }

        RestoreIF(uFlags);

        *pResult = WaitResult_OK;
        *pIndex = i;
        if (NULL != pState) *pState = uState;
        return;
    }

    WaiterReadyToSleep(pResult);
    if (WaitResult_OK != *pResult) {
        RestoreIF(uFlags);
        return;
    }
    RestoreIF(uFlags);

    Schedule();

    if (ClearInterrupted()) {
        *pResult = WaitResult_Interrupted;
        return;
    }

    *pResult = WaitResult_OK;
    *pIndex = GetCurrentThread()->GetNotifiedEventIndex();
    if (NULL != pState) *pState = GetCurrentThread()->GetEventState();
}

void DzEventSet::TryUninterruptibleWait(
    uint_t ticks, WaitResult *pResult, Int32 *pIndex, EventState *pState)
{
    assert(pResult);
    assert(pIndex);

    uint_t i, uState;

    DzTimer timer(ScheduleTimeout, GetCurrentThread());

    uint32_t uFlags = SaveFlagsAndCli();
    for (i = 0; i < m_uNumberOfEvents; i++) {
        register DzEvent *pEvent = m_pEvents[i];

        if (pEvent->m_bManualReset) {
            if (UNSIGNALED == (uState = pEvent->m_uState)) {
                continue;
            }
        }
        else {
            if (UNSIGNALED == (uState = InterlockedExchange(
                    (Int32 *)&pEvent->m_uState, UNSIGNALED))) {
                continue;
            }
        }

        RestoreIF(uFlags);

        *pResult = WaitResult_OK;
        *pIndex = i;
        if (NULL != pState) *pState = uState;
        return;
    }

    if (0 == ticks) {
        RestoreIF(uFlags);
        *pResult = WaitResult_TimedOut;
        return;
    }
    timer.Set(ticks);
    WaiterReadyToUninterruptibleSleep(&timer);
    RestoreIF(uFlags);

    Schedule();

    if (timer.Due()) {
        *pResult = WaitResult_TimedOut;
        return;
    }

    *pResult = WaitResult_OK;
    *pIndex = GetCurrentThread()->GetNotifiedEventIndex();
    if (NULL != pState) *pState = GetCurrentThread()->GetEventState();
}

void DzEventSet::TryWait(
    uint_t ticks, WaitResult *pResult, Int32 *pIndex, EventState *puState)
{
    assert(pResult);
    assert(pIndex);

    uint_t i, uState;

    DzTimer timer(ScheduleTimeout, GetCurrentThread());

    uint32_t uFlags = SaveFlagsAndCli();
    for (i = 0; i < m_uNumberOfEvents; i++) {
        register DzEvent *pEvent = m_pEvents[i];

        if (pEvent->m_bManualReset) {
            if (UNSIGNALED == (uState = pEvent->m_uState)) {
                continue;
            }
        }
        else {
            if (UNSIGNALED == (uState = InterlockedExchange(
                    (Int32 *)&pEvent->m_uState, UNSIGNALED))) {
                continue;
            }
        }

        RestoreIF(uFlags);

        *pResult = WaitResult_OK;
        *pIndex = i;
        if (NULL != puState) *puState = uState;
        return;
    }

    if (0 == ticks) {
        RestoreIF(uFlags);
        *pResult = WaitResult_TimedOut;
        return;
    }
    timer.Set(ticks);
    WaiterReadyToSleep(&timer, pResult);
    if (WaitResult_OK != *pResult) {
        RestoreIF(uFlags);
        return;
    }
    RestoreIF(uFlags);

    Schedule();

    if (ClearInterrupted()) {
        *pResult = WaitResult_Interrupted;
        return;
    }

    if (timer.Due()) {
        *pResult = WaitResult_TimedOut;
        return;
    }

    *pResult = WaitResult_OK;
    *pIndex = GetCurrentThread()->GetNotifiedEventIndex();
    if (NULL != puState) *puState = GetCurrentThread()->GetEventState();
}

EXTERN void DzWaitForAnyEvent(
    Int32 uNumberOfEvents, DzEvent **pEvents,
    WaitResult *pResult, Int32 *pIndex, EventState *pState)
{
    assert(0 < uNumberOfEvents && uNumberOfEvents <= MAXIMUM_WAIT_EVENTS);
    assert(pEvents);
    assert(pResult);
    assert(pIndex);

    if (1 == uNumberOfEvents) {
        pEvents[0]->Wait(pResult, pState);
        if (WaitResult_OK == *pResult) *pIndex = 0;
    }
    else {
        EventSetRefNode eventSetRefNodes[MAXIMUM_WAIT_EVENTS];
        DzEventSet eventSet(uNumberOfEvents, pEvents, eventSetRefNodes);
        eventSet.Wait(pResult, pIndex, pState);
    }
}

EXTERN void DzTryWaitForAnyEvent(
    uint_t uNumberOfEvents, DzEvent **pEvents, Millisecond32 ticks,
    WaitResult *pResult, Int32 *pIndex, EventState *pState)
{
    assert(0 < uNumberOfEvents && uNumberOfEvents <= MAXIMUM_WAIT_EVENTS);
    assert(pEvents);
    assert(pResult);
    assert(pIndex);

    if (1 == uNumberOfEvents) {
        pEvents[0]->TryWait(ticks, pResult, pState);
        if (WaitResult_OK == *pResult) *pIndex = 0;
    }
    else {
        EventSetRefNode eventSetRefNodes[MAXIMUM_WAIT_EVENTS];
        DzEventSet eventSet(uNumberOfEvents, pEvents, eventSetRefNodes);
        eventSet.TryWait(ticks, pResult, pIndex, pState);
    }
}
